package hku.cs.lunuo.instrument;

import hku.cs.lunuo.profile.Coverage;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import soot.Body;
import soot.BodyTransformer;
import soot.Unit;
import soot.tagkit.LineNumberTag;
import soot.tagkit.Tag;
import soot.toolkits.graph.Block;
import soot.toolkits.graph.BlockGraph;
import soot.toolkits.graph.ExceptionalBlockGraph;

public class CoverageInstrument extends BodyTransformer {

	private static CoverageInstrument instance = new CoverageInstrument();

	private CoverageInstrument() {
	}

	public static CoverageInstrument v() {
		return instance;
	}

	@Override
	protected void internalTransform(Body b, String arg1, Map arg2) {

		BlockGraph bGraph = new ExceptionalBlockGraph(b);
		Iterator<Block> blockIt = bGraph.getBlocks().iterator();
		
		String methodName = b.getMethod().toString();
		String className = methodName.substring(1, methodName.indexOf(":"));
		methodName = methodName.substring(methodName.indexOf(":") + 2,
				methodName.length() - 1);

		List<Block> blockLs = bGraph.getBlocks();
		
		HashMap<Integer, ArrayList<Set<Integer>>> inInHelp = new HashMap<Integer, ArrayList<Set<Integer>>>();
		
		for (int bi = 0; bi < blockLs.size(); bi++) {
			Block block = blockLs.get(bi);
			
			List<Block> preBls = block.getPreds();
			Set<Integer> preSet = new HashSet<Integer>();
			
			for(int i=0; i<preBls.size(); i++){
				preSet.add(preBls.get(i).getIndexInMethod());
			}
			
			List<Block> succBls = block.getSuccs();
			Set<Integer> succSet = new HashSet<Integer>();
			for(int i=0; i<succBls.size(); i++){
				succSet.add(succBls.get(i).getIndexInMethod());
			}
			
			Iterator<Unit> uIt = block.iterator();
			Set<Integer> unitSet = new HashSet<Integer>();
			while(uIt.hasNext()){
				Unit u = uIt.next();
				
				Tag t = u.getTag("LineNumberTag");
				if(t!=null){
					unitSet.add(((LineNumberTag)t).getLineNumber());
				}
			}
			ArrayList<Set<Integer>> aList = new ArrayList<Set<Integer>>(3);
			aList.add(preSet);
			aList.add(succSet);
			aList.add(unitSet);
			inInHelp.put(bi, aList);
		}
		
		
		HashMap<String, HashMap<Integer, ArrayList<Set<Integer>>>> inHelp = 
			Coverage.coverage.get(className);
		if(inHelp==null){
			inHelp = new HashMap<String, HashMap<Integer, ArrayList<Set<Integer>>>>();
		}
		inHelp.put(methodName, inInHelp);
		Coverage.coverage.put(className, inHelp);
	}

}